/****************************************************************
 *  This file created by writerB.php on 03/20/2007 at 22:47:44  *
 ****************************************************************
 *  writerB.php created by Tonk (tonk@ctonk.com) in 2007        *
 ****************************************************************
 *                 General Document Information
 *
 *  File Name: character.cpp
 *  Class Name: Character
 *  Parent Classes: Creature 
 *  Data members: 39
 *  Notes:
 ****************************************************************/

#include <cstdarg>
#include <cstring>
#include <iostream>
#include <vector>
#include "definitions.h"
#include "combined.h"
#include "log.h"
#include "note.h"
#include "socket.h"
#include "stringutil.h"
#include "timestamp.h"
#include "world.h"



int Character::numCharacters = 0;
int Character::characterIndex = 0;


// Default constructor...
Character::Character( bool copyover ): Creature::Creature() {
	setId( 0 );
	setGotInput( false );
	setDisconnected( false );
	setFlags( 0 );
	setSocket( ( copyover ) ? new Socket : NULL );
	setRoom( NULL );
	setBoard( NULL );
	setTedit( false );
	setStatus( 0 );
	setBankGold( 0 );
	setBankSilver( 0 );
	setMobDeaths( 0 );
	setMobKills( 0 );
	setPkDeaths( 0 );
	setPkKills( 0 );
	setExp( 0 );
	setTnl( 0 );
	setPractices( 0 );
	setTrains( 0 );
	setGains( 0 );
	setHand( 0 );
	setPoints( 0 );
	setEcho( '\0' );
	// Increment the object count...
	numCharacters++;
}

// Additional constructors...

// Copy constructor (Doesn't change _CharacterID)...
Character::Character( const Character& ref ): Creature::Creature( ref ) {
	setId( ref.getId() );
	setGotInput( ref.getGotInput() );
	setDisconnected( ref.getDisconnected() );
	setReply( ref.getReply() );
	setLastInput( ref.getLastInput() );
	setRepeat( ref.getRepeat() );
	setPrompt( ref.getPrompt() );
	setPoofin( ref.getPoofin() );
	setPoofout( ref.getPoofout() );
	setPassword( ref.getPassword() );
	setLastName( ref.getLastName() );
	setProfession( ref.getProfession() );
	setClan( ref.getClan() );
	setTitle( ref.getTitle() );
	setEyeDesc( ref.getEyeDesc() );
	setEyeColor( ref.getEyeColor() );
	setHairDesc( ref.getHairDesc() );
	setHairColor( ref.getHairColor() );
	setHouse( ref.getHouse() );
	setRoom( NULL );
	setSocket( ref.getSocket() );
	setObject( ref.getObject() );
	setBoard( ref.getBoard() );
	setNote( ref.getNote() );
	setFlags( ref.getFlags() );
	setStatus( ref.getStatus() );
	setBankGold( ref.getBankGold() );
	setBankSilver( ref.getBankSilver() );
	setMobDeaths( ref.getMobDeaths() );
	setMobKills( ref.getMobKills() );
	setPkDeaths( ref.getPkDeaths() );
	setPkKills( ref.getPkKills() );
	setExp( ref.getExp() );
	setTnl( ref.getTnl() );
	setPractices( ref.getPractices() );
	setTrains( ref.getTrains() );
	setGains( ref.getGains() );
	setHand( ref.getHand() );
	setPoints( ref.getPoints() );
	setEcho( ref.getEcho() );
	// Increment the object count...
	numCharacters++;
}

// Destructor...
Character::~Character() {
	if ( getSocket() ) {
		if ( !getSocket()->GetSocketIP().empty() ) {
			SocketServer::Instance()._socketList.remove( getSocket() );
			SocketServer::Instance().KillSocket( getSocket() );
			if ( getStatus() < CONNECTED )
				log( getSocket()->GetSocketIP() + " has disconnected.", SERVER );
			else
				log( getName() + " has disconnected from " + getSocket()->GetSocketIP(), SERVER );
		}
		delete getSocket();
		setSocket( NULL );
	}
	// Decrement the object count...
	numCharacters--;
}

// Assignment operator (Doesn't change _CharacterID)...
Character& Character::operator = ( const Character& ref ) {
	setId( ref.getId() );
	setGotInput( ref.getGotInput() );
	setDisconnected( ref.getDisconnected() );
	setReply( ref.getReply() );
	setLastInput( ref.getLastInput() );
	setRepeat( ref.getRepeat() );
	setPrompt( ref.getPrompt() );
	setPoofin( ref.getPoofin() );
	setPoofout( ref.getPoofout() );
	setPassword( ref.getPassword() );
	setLastName( ref.getLastName() );
	setProfession( ref.getProfession() );
	setClan( ref.getClan() );
	setTitle( ref.getTitle() );
	setEyeDesc( ref.getEyeDesc() );
	setEyeColor( ref.getEyeColor() );
	setHairDesc( ref.getHairDesc() );
	setHairColor( ref.getHairColor() );
	setHouse( ref.getHouse() );
	setRoom( ref.getRoom() );
	setSocket( ref.getSocket() );
	setObject( ref.getObject() );
	setBoard( ref.getBoard() );
	setNote( ref.getNote() );
	setFlags( ref.getFlags() );
	setStatus( ref.getStatus() );
	setBankGold( ref.getBankGold() );
	setBankSilver( ref.getBankSilver() );
	setMobDeaths( ref.getMobDeaths() );
	setMobKills( ref.getMobKills() );
	setPkDeaths( ref.getPkDeaths() );
	setPkKills( ref.getPkKills() );
	setExp( ref.getExp() );
	setTnl( ref.getTnl() );
	setPractices( ref.getPractices() );
	setTrains( ref.getTrains() );
	setGains( ref.getGains() );
	setHand( ref.getHand() );
	setPoints( ref.getPoints() );
	setEcho( ref.getEcho() );
	return *this;
}

// Protected Methods...
void Character::setId( const unsigned& id ) { // protected
	_id = id;
}

// Public accessor methods...
unsigned Character::getId( void ) const { // public
	return _id;
}

void Character::setGotInput( const bool& gotInput ) {
	_gotInput = gotInput;
	return;
}

bool Character::getGotInput( void ) const {
	return _gotInput;
}

void Character::setDisconnected( const bool& disconnected ) {
	_disconnected = disconnected;
	return;
}

bool Character::getDisconnected( void ) const {
	return _disconnected;
}

void Character::setReply( const std::string& reply ) {
	_reply = reply;
	return;
}

std::string Character::getReply( void ) const {
	return _reply;
}

void Character::setLastInput( const std::string& lastInput ) {
	_lastInput = lastInput;
	return;
}

std::string Character::getLastInput( void ) const {
	return _lastInput;
}

void Character::setRepeat( const std::string& repeat ) {
	_repeat = repeat;
	return;
}

std::string Character::getRepeat( void ) const {
	return _repeat;
}

void Character::setPrompt( const std::string& prompt ) {
	_prompt = prompt;
	return;
}

std::string Character::getPrompt( void ) const {
	return _prompt;
}

void Character::setPoofin( const std::string& poofin ) {
	_poofin = poofin;
	return;
}

std::string Character::getPoofin( void ) const {
	return _poofin;
}

void Character::setPoofout( const std::string& poofout ) {
	_poofout = poofout;
	return;
}

std::string Character::getPoofout( void ) const {
	return _poofout;
}

void Character::setPassword( const std::string& password ) {
	_password = password;
	return;
}

std::string Character::getPassword( void ) const {
	return _password;
}

void Character::setLastName( const std::string& lastName ) {
	_lastName = lastName;
	return;
}

std::string Character::getLastName( void ) const {
	return _lastName;
}

void Character::setProfession( const std::string& profession ) {
	_profession = profession;
	return;
}

std::string Character::getProfession( void ) const {
	return _profession;
}

void Character::setClan( const std::string& clan ) {
	_clan = clan;
	return;
}

std::string Character::getClan( void ) const {
	return _clan;
}

void Character::setTitle( const std::string& title ) {
	_title = title;
	return;
}

std::string Character::getTitle( void ) const {
	return _title;
}

void Character::setEyeDesc( const std::string& eyeDesc ) {
	_eyeDesc = eyeDesc;
	return;
}

std::string Character::getEyeDesc( void ) const {
	return _eyeDesc;
}

void Character::setEyeColor( const std::string& eyeColor ) {
	_eyeColor = eyeColor;
	return;
}

std::string Character::getEyeColor( void ) const {
	return _eyeColor;
}

void Character::setHairDesc( const std::string& hairDesc ) {
	_hairDesc = hairDesc;
	return;
}

std::string Character::getHairDesc( void ) const {
	return _hairDesc;
}

void Character::setHairColor( const std::string& hairColor ) {
	_hairColor = hairColor;
	return;
}

std::string Character::getHairColor( void ) const {
	return _hairColor;
}

void Character::setHouse( const std::string& house ) {
	_house = house;
	return;
}

std::string Character::getHouse( void ) const {
	return _house;
}

void Character::setRoom( Room* room ) {
	if ( room ) {
		// It's easier to check for REDIT status and area permissions here, then
		// to check them everywhere in the REDIT code itself...
		if ( getStatus() == REDIT && !room->getArea()->hasPermission( this ) )
			setStatus( CONNECTED );
	}
	_room = room;
	return;
}

void Character::setSocket( Socket* socket ) {
	_socket = socket;
	return;
}

Socket* Character::getSocket( void ) const {
	return _socket;
}

void Character::setObject( Object* object ) {
	_object = object;
	return;
}

Object* Character::getObject( void ) const {
	return _object;
}

void Character::setBoard( Board* board ) {
	_board = board;
	return;
}

Board* Character::getBoard( void ) const {
	return _board;
}

void Character::setNote( Note* note ) {
	_note = note;
	return;
}

Note* Character::getNote( void ) const { // public
	return _note;
}

std::vector< Note* >& Character::getNotes( void ) { // public
	return _notes;
}

void Character::setFlags( const unsigned& flags ) {
	_flags = flags;
	return;
}

unsigned Character::getFlags( void ) const {
	return _flags;
}

void Character::setTedit( const bool& tedit ) {
	setFlags( ( tedit ) ? (getFlags() | CH_FLAG_TEDIT) : ((~CH_FLAG_TEDIT) & getFlags()) );
	return;
}

bool Character::getTedit( void ) const {
	return ( (getFlags() & CH_FLAG_TEDIT) == CH_FLAG_TEDIT ) ? true: false;
}

void Character::setOmninet( const bool& omninet ) { // public
	setFlags( ( omninet ) ? (getFlags() | CH_FLAG_OMNINET) : ((~CH_FLAG_OMNINET) & getFlags()) );
	return;
}

void Character::setOmninet( const unsigned& which, const bool& set ) { // public
	setFlags( ( set ) ? (getFlags() | which) : ((~which) & getFlags()) );
	return;
}

bool Character::getOmninet( void ) const { // public
	return ( (getFlags() & CH_FLAG_OMNINET) == CH_FLAG_OMNINET ) ? true : false;
}

bool Character::getOmninet( const unsigned& which ) const { // public
	return ( (getFlags() & which) == which ) ? true : false;
}

void Character::setStatus( const unsigned& status ) {
	_status = status;
	return;
}

unsigned Character::getStatus( void ) const {
	return _status;
}

void Character::setBankGold( const unsigned& bankGold ) {
	_bankGold = bankGold;
	return;
}

unsigned Character::getBankGold( void ) const {
	return _bankGold;
}

void Character::setBankSilver( const unsigned& bankSilver ) {
	_bankSilver = bankSilver;
	return;
}

unsigned Character::getBankSilver( void ) const {
	return _bankSilver;
}

void Character::setMobDeaths( const unsigned& mobDeaths ) {
	_mobDeaths = mobDeaths;
	return;
}

unsigned Character::getMobDeaths( void ) const {
	return _mobDeaths;
}

void Character::setMobKills( const unsigned& mobKills ) {
	_mobKills = mobKills;
	return;
}

unsigned Character::getMobKills( void ) const {
	return _mobKills;
}

void Character::setPkDeaths( const unsigned& pkDeaths ) {
	_pkDeaths = pkDeaths;
	return;
}

unsigned Character::getPkDeaths( void ) const {
	return _pkDeaths;
}

void Character::setPkKills( const unsigned& pkKills ) {
	_pkKills = pkKills;
	return;
}

unsigned Character::getPkKills( void ) const {
	return _pkKills;
}

void Character::setExp( const unsigned& exp ) {
	_exp = exp;
	return;
}

unsigned Character::getExp( void ) const {
	return _exp;
}

void Character::setTnl( const unsigned& tnl ) {
	_tnl = tnl;
	return;
}

unsigned Character::getTnl( void ) const {
	return _tnl;
}

void Character::setPractices( const unsigned& practices ) {
	_practices = practices;
	return;
}

unsigned Character::getPractices( void ) const {
	return _practices;
}

void Character::setTrains( const unsigned& trains ) {
	_trains = trains;
	return;
}

unsigned Character::getTrains( void ) const {
	return _trains;
}

void Character::setGains( const unsigned& gains ) {
	_gains = gains;
	return;
}

unsigned Character::getGains( void ) const {
	return _gains;
}

void Character::setHand( const unsigned& hand ) {
	_hand = hand;
	return;
}

unsigned Character::getHand( void ) const {
	return _hand;
}

void Character::setPoints( const unsigned& points ) {
	_points = points;
	return;
}

unsigned Character::getPoints( void ) const {
	return _points;
}

void Character::setEcho( const char& echo ) { // public
	_echo = echo;
	return;
}

char Character::getEcho( void ) const { // public
	return _echo;
}

// General methods...
void Character::Send( char* message, ... ) {
	va_list args;
	char buf[MAX_BUFFER];
	std::string buf2;

    va_start( args, message );

	vsprintf( buf, message, args );
	buf2 = getSocket()->GetOutput();
	buf2.append( buf );
	
	getSocket()->SetOutput( buf2 );
	va_end( args );
}

void Character::Send( const std::string &message ) {
	std::string buf;
	buf.append( message );	
	getSocket()->SetOutput( buf );
}

void Character::flushOutput( ) {
	std::string output;
	std::string buf;
	std::string buf2;
	std::string buf3;
	std::string colorClear;
	// This will add a prompt to the end of output.
	// Of course, one needs some text before it'll add a prompt or we
	// can add a prompt if we get some input and it calls for it. :)
	
	output = getSocket()->GetOutput();
	if ( output.empty() != true || _gotInput ) {
		//if ( this->waiting == false )  { // Don't send a prompt if there's delayed text going
			if ( !getGotInput() ) {
					output.insert( 0, "\n\r" );
			} else {
				// Only if there's no input set gotInput to false
				// we do it like this so there isn't an extra newline
				// everytime you get some output
				if ( getSocket()->GetInput().empty() )
					setGotInput( false );
			}//*/
			//output.append( "\n\r" + _handlers.front()->Prompt( (this) ) );
			switch ( getStatus() ) {
				case LOGIN:	// Do nothing
					break;
				case AEDIT:
					if ( getArea() == NULL )
						buf << "{x\n\r[no area selected]";
					else
						buf << "{x\n\r[area " << getArea()->getKeyword() << ']';
					break;
				case MEDIT:
					buf << "{x\n\r[mob " << getMob()->getVnum() << ']';
					break;
				case PEDIT:
					buf << "{x\n\r[player " << getCharacter()->getName() << ']';
					break;
				case REDIT:
					buf << "{x\n\r[room " << getRoom()->getVnum() << ']';
					break;
				default:
					buf << formatPrompt();
					break;
			}
			if ( getTedit() )
				buf = "[symphony text editor]";
			if ( getStatus() != LOGIN )
				output << "\n\r" << buf << "{x ";
			getSocket()->SetOutput( output );
		//}
	}

	// color parsing...
	std::string temp = getSocket()->GetOutput();
	for ( int x=0; x < (int)temp.size(); x++ ) {
		if ( temp[x] == '{' ) {
			buf3 << cprint_private( 'x' );
			if ( temp[x+1] == '!' || temp[x+1] == '@' ) {
				buf3 << cprint_private( temp[x+1], temp[x+2] );
				x += 2;
			}
			else {
				buf3 << cprint_private( temp[x+1] );
				x++;
			}
		}
		else
			buf3 << temp[x];
	}
	getSocket()->SetOutput( buf3 );

	return;
}

std::string Character::promptSwitch( char c ) {
	std::string replaced;

	switch ( c ) {
		case 'h': replaced << getHp(); break;
		case 'H': replaced << getMaxHp(); break;
		case 'm': replaced << getMana(); break;
		case 'M': replaced << getMaxMana(); break;
		case 'v': replaced << getMove(); break;
		case 'V': replaced << getMaxMove(); break;
		case 'g': replaced << getGold(); break;
		case 'G': replaced << getBankGold(); break;
		case 's': replaced << getSilver(); break;
		case 'S': replaced << getBankSilver(); break;
		case 'r': replaced << getRoom()->getVnum(); break;
		default: replaced << '%'; break;
	}

	return replaced;
}

std::string Character::formatPrompt( void ) {
	std::string unformatted = getPrompt();
	std::string formatted = "{x\n\r";

	for ( unsigned x=0; x < unformatted.size(); x++ ) {
		if ( unformatted[x] == '%' ) {
			formatted << Character::promptSwitch( unformatted[x+1] );
			x++;
		}
		else
			formatted << unformatted[x];
	}

	return formatted;
}

Note* Character::addNote( void ) { // public
	Note* note = new Note;
	getNotes().push_back( note );
	return note;
}

bool Character::save( void ) { // public
	std::string file = PFILE_DIR;
	file << lowerString( getName() ) << ".pfile";
	std::ofstream write( file.c_str() );
	if ( write.fail() ) {
		log( "Couldn't open " + file + " for writing.", SERVER );
		record( "Couldn't open " + file + " for writing." );
		return false;
	}
	Creature::save( write );

	write << "// Character data...\n";
	write	<< getId() << ' ' << getPassword() << '\n'
			<< getPrompt() << '\n' << getLastName() << '\n'
			<< getProfession() << '\n' << getClan() << '\n';

	write << getBankGold() << ' ' << getBankSilver() << '\n';
	write << getMobDeaths() << ' ' << getMobKills() << ' ' << getPkDeaths() << ' ' << getPkKills() << '\n';
	write << getExp() << ' ' << getTnl() << '\n';
	write << getPractices() << ' ' << getTrains() << ' ' << getGains() << '\n';
	write << getOmninet() << '\n';
	write << getTitle() << '\n';
	write << getEyeDesc() << ' ' << getEyeColor() << '\n';
	write << getHairDesc() << ' ' << getHairColor() << '\n';
	write << getStrHeight() << ' ' << getStrWeight() << ' ' << getHand() << '\n';
	write << getHouse() << '\n';
	write << getPoints() << ' ' << getEcho() << '\n';
	write << getPoofin() << '\n' << getPoofout() << '\n';

	write << "\n// Unsent notes...";
	for ( std::vector< Note* >::iterator it = getNotes().begin(); it != getNotes().end(); it++ )
		write << *it;

	write << "\n\n// File written " << Timestamp::Instance().getDateTime() << '\n';

	write.close();
	write.clear();

    return true;
}

bool Character::load( void ) { // public														FIXME
	std::string	file;
	char		password[2048], lastName[2048], clan[2048], profession[2048], title[2048],
				eyeDesc[2048], eyeColor[2048], hairDesc[2048], hairColor[2048], prompt[2048],
				strHeight[2048], strWeight[2048], house[2048], poofin[2048], poofout[2048], temp[2048];
	unsigned	id=0, bankGold=0, bankSilver=0, mobDeaths=0, mobKills=0,
				pkDeaths=0, pkKills=0, exp=0, tnl=0, practices=0, trains=0, gains=0,
				hand=0, points=0;
	bool		omninet;
	char		echo;

	file << PFILE_DIR << lowerString( getName() ) << ".pfile";

	std::ifstream read( file.c_str() );
	if ( read.fail() ) {
		log( "Couldn't open " + file + " for reading.", SERVER );
		record( "Couldn't open " + file + " for reading." );
		return false;
	}

	// Read in Creature data...
	Creature::load( read );

	// Put this Character in the room that was read in from Creature::load()
	getRoom()->add( this );

	// Read the Character data in...
	read >> id >> password;
	read.get(); read.getline( prompt, 1000 );
	read.getline( lastName, 1000 );
	read >> profession;
	read >> clan;
	read >> bankSilver >> bankGold;
	read >> mobDeaths >> mobKills >> pkDeaths >> pkKills;
	read >> exp >> tnl;
	read >> practices >> trains >> gains;
	read >> omninet;
	read.get(); read.getline( title, 1000 );
	read >> eyeDesc >> eyeColor;
	read >> hairDesc >> hairColor;
	read >> strHeight >> strWeight >> hand;
	read.get(); read.getline( house, 1000 );
	read >> points >> echo;
	read.get(); read.getline( poofin, 1000 );
	read.getline( poofout, 1000 );

	// Set our newly found Character data...
	setId( id );
	setPassword( password );
	setPrompt( prompt );
	setLastName( lastName );
	setProfession( profession );
	setClan( clan );
	setBankSilver( bankSilver );
	setBankGold( bankGold );
	setMobDeaths( mobDeaths );
	setMobKills( mobKills );
	setPkDeaths( pkDeaths );
	setPkKills( pkKills );
	setExp( exp );
	setTnl( tnl );
	setPractices( practices );
	setTrains( trains );
	setGains( gains );
	setOmninet( omninet );
	setTitle( title );
	setEyeDesc( eyeDesc );
	setEyeColor( eyeColor );
	setHairDesc( hairDesc );
	setHairColor( hairColor );
	setStrHeight( strHeight );
	setStrWeight( strWeight );
	setHand( hand );
	setHouse( house );
	setPoints( points );
	setEcho( echo );
	setPoofin( poofin );
	setPoofout( poofout );

	// Load unsent notes...
	while ( read.peek() != EOF ) {
		read.getline( temp, 10000 );
		if ( strcmp( temp, "**NOTE" ) == 0 )
			read >> addNote();
	}

	read.close();
	read.clear();

	return true;
}

void Character::record( const std::string& message ) { // public
	std::string file;

	file = ( getLevel() < BUILDER ) ? PLAYER_LOG_DIR : ADMIN_LOG_DIR;
	file << getName() << ".log";

	std::ofstream write( file.c_str(), std::ofstream::app );
	if ( write.fail() ) {
		default_log( message, file );
		return;
	}
	write << Timestamp::Instance().getDateTime() << "\n\t" << message << '\n';

	write.close();
	write.clear();

	return;
}

// Static methods...
std::string Character::hand2string( const unsigned& hand ) {
	switch ( hand ) {
		case LEFT:	return "left";
		case RIGHT:	return "right";
		default:
			std::string message;
			message << "Default hit in Character::hand2string() in " << __FILE__ << " line " << __LINE__;
			log( message, SERVER );
			return "{RERROR{x";
	}
}

// Associated operators...
std::ostream& operator << ( std::ostream& write, Character& character ) { // associated

	return write;
}

inline std::ostream& operator << ( std::ostream& write, Character* character ) { // associated
	return ( write << *character );
}

std::istream& operator >> ( std::istream& read, Character& character ) { // associated

	return read;
}

inline std::istream& operator >> ( std::istream& read, Character* character ) { // associated
	return ( read >> *character );
}
